﻿using ExampleService.VolcanoEngine.SDK.Dto;
using ExampleService.VolcanoEngine.SDK.Utils;
using Microsoft.Extensions.Options;
using System.Text;

namespace ExampleService.VolcanoEngine.SDK
{
    public class VolcanoEngine
    {
        private readonly VolcanoOptions volcanoOptions;

        public VolcanoEngine(IOptions<VolcanoOptions> volcanoOptions)
        {
            this.volcanoOptions = volcanoOptions.Value;
        }

        /// <summary>
        /// 获取 授权头 Token
        /// </summary>
        /// <param name="requestBody"></param>
        /// <returns></returns>
        internal string BuildAuthorization(string requestBody, string nowDate, string dateTimeSignStr)
        {
            var headers = GetHeaders();
            string canonicalHeaderStr = GetCanonicalHeaderStr(headers, requestBody, dateTimeSignStr);
            string signHeaderStr = string.Join(";", headers);

            // 1，规范请求
            var canonicalRequest = GetCanonicalRequest(requestBody, canonicalHeaderStr, signHeaderStr);
            // 2，待签名字符串
            var stringToSign = GetStringToSign(canonicalRequest, nowDate, dateTimeSignStr);
            // 3，派生签名密钥
            var signingKey = GetSigningKey(nowDate);
            // 4，计算签名
            var signature = GetSignature(stringToSign, signingKey);

            return $"{ConstKey.Algorithm} Credential={volcanoOptions.ApiKey}/{nowDate}/{ConstKey.Region}/{ConstKey.Service}/request, " +
                 $"SignedHeaders={signHeaderStr}, Signature={signature}";
        }

        /// <summary>
        /// 2，规范请求
        /// </summary>
        /// <param name="requestBody">请求体</param>
        /// <param name="canonicalHeaderStr">规范头</param>
        /// <param name="signHeaderStr">签名头</param> 
        /// <returns></returns>
        private string GetCanonicalRequest(string requestBody, string canonicalHeaderStr, string signHeaderStr)
        {
            var canonicalRequest = string.Join('\n',
             "POST",
             "/",
             ConstKey.UrlQuery,
             canonicalHeaderStr,
             signHeaderStr,
             Encry256.ComputeHash256(requestBody));

            return canonicalRequest;
        }

        /// <summary>
        /// 3,待签名字符串
        /// </summary>
        /// <param name="canonicalRequest">规范请求</param>
        /// <param name="nowDate">请求日期</param>
        /// <param name="dateTimeSignStr">时间格式</param>
        /// <returns></returns>
        private string GetStringToSign(string canonicalRequest, string nowDate, string dateTimeSignStr)
        {
            var stringToSign = string.Join('\n',
                ConstKey.Algorithm,
                dateTimeSignStr,
                $"{nowDate}/{ConstKey.Region}/{ConstKey.Service}/request",
                Encry256.ComputeHash256(canonicalRequest));
            return stringToSign;
        }

        /// <summary>
        /// 4,派生签名密钥
        /// </summary>
        /// <param name="nowDate">请求日期</param>
        /// <returns></returns>
        private byte[] GetSigningKey(string nowDate)
        {
            var kDate = Encry256.HmacSha256(nowDate, Encoding.UTF8.GetBytes(volcanoOptions.ApiSecret));
            var kRegion = Encry256.HmacSha256(ConstKey.Region, kDate);
            var kService = Encry256.HmacSha256(ConstKey.Service, kRegion);
            var kSigning = Encry256.HmacSha256("request", kService);

            return kSigning;
        }

        /// <summary>
        /// 5,计算签名
        /// </summary>
        /// <param name="stringToSign">待签名字符串</param>
        /// <param name="signingKey">派生签名密钥</param>
        /// <returns></returns>
        private string GetSignature(string stringToSign, byte[] signingKey)
        {
            var signatureBytes = Encry256.HmacSha256(stringToSign, signingKey);
            var signature = Convert.ToHexString(signatureBytes).ToLower();
            return signature;
        }

        /// <summary>
        /// 获取规范头字符串
        /// </summary>
        /// <param name="headers">请求头</param>
        /// <param name="requestBody">请求体</param>
        /// <param name="dateTimeSignStr">时间格式</param>
        /// <returns></returns>
        private string GetCanonicalHeaderStr(List<string> headers, string requestBody, string dateTimeSignStr)
        {
            var builder = new StringBuilder();

            foreach (var header in headers)
            {
                var headerValue = string.Empty;
                switch (header)
                {
                    case "host":
                        headerValue = ConstKey.Host;
                        break;
                    case "content-type":
                        headerValue = "application/json";
                        break;
                    case "x-date":
                        headerValue = dateTimeSignStr;
                        break;
                    case "x-content-sha256":
                        headerValue = Encry256.ComputeHash256(requestBody);
                        break;
                    default:
                        headerValue = header;
                        break;
                }
                builder.Append(header).Append(':').Append(headerValue).Append('\n');
            }

            return builder.ToString();
        }

        /// <summary>
        /// 获取签名头列表
        /// </summary>
        /// <returns></returns>
        private List<string> GetHeaders()
        {
            var headers = new List<string>();
            headers.Add("host");
            headers.Add("content-type");
            headers.Add("x-date");
            headers.Add("x-content-sha256");

            headers.Sort();

            return headers;
        }
    }
}
